package server;

import classLoader.MyClassLoader;
import demacia.Context;
import demacia.Host;
import demacia.Mapper;
import demacia.Wrapper;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * @program: minicat
 * @description
 * @author: luffy
 * @create: 2020-08-02 20:10
 */
public class BootStrap extends ClassLoader {

    private Map<String, HttpServlet> servletMap = new HashMap<>();

    private Mapper mapper = new Mapper();

    private int port;

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    /**
     * MiniCat启动需要初始化展开的一些操作
     */
    public void start() throws Exception {
        loadServlet();


//        定义一个线程池
        int corePoolSize = 10;
        int maximumPoolSize = 50;
        long keepAliveTime = 100L;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler
        );
        /**
         * 完成 MiniCat1.0版本（浏览器请求http://localhost:8080，返回一个固定的字符串到页面"Hello MiniCat"）
         *
         */

        ServerSocket serverSocket = new ServerSocket(port);

        /*while (true) {
            Socket socket = serverSocket.accept();
            OutputStream outputStream = socket.getOutputStream();
            String data = "Hello MiniCat";
            String httpHeader200 = HttpProtocolUtil.getHttpHeader200(data.getBytes().length)+data;
            outputStream.write(httpHeader200.getBytes());
            socket.close();
        }*/


//         完成MiniCat 2.0版本
//         需求：封装Request和Response对象

    /*while (true) {
        Socket socket = serverSocket.accept();
        InputStream inputStream = socket.getInputStream();

        Request request = new Request(inputStream);
        Response response = new Response(socket.getOutputStream());

        response.outPutHtml(request.getUrl());

        socket.close();

    }*/

        /**
         * MiniCat3.0版本
         * 需求：可以请求动态资源（servlet）
         */
//        while (true) {
//            Socket socket = serverSocket.accept();
//            InputStream inputStream = socket.getInputStream();
//
//            Request request = new Request(inputStream);
//            Response response = new Response(socket.getOutputStream());
//
//            if (servletMap.get(request.getUrl()) == null) {
//                response.outPutHtml(request.getUrl());
//            }else {
//                HttpServlet httpServlet = servletMap.get(request.getUrl());
//                httpServlet.service(request,response);
//            }
//            socket.close();
//        }
        /**
         * 多线程改装（不适用线程池）
         */
       /* while (true) {
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket,servletMap);
            requestProcessor.start();
        }*/
        /**
         * 使用线程池
         */
        while (true) {
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket, mapper);
            threadPoolExecutor.execute(requestProcessor);
        }

    }


    /**
     * 加载解析web.xml,初始化servlet
     */
    private void loadServlet() throws Exception {
        Host host = new Host();
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        SAXReader saxReader = new SAXReader();

        Document document = saxReader.read(resourceAsStream);
        Element rootElement = document.getRootElement();
        Element connectorElement = (Element) rootElement.selectSingleNode("//Connector");
        String connectorPort = connectorElement.attributeValue("port");
        port = Integer.valueOf(connectorPort);

        Element hostElement = (Element) rootElement.selectSingleNode("//Host");
        String name = hostElement.attributeValue("name");
        String appBase = hostElement.attributeValue("appBase");
        host.setName(name + ":" + port);
        File webappFiles = new File(appBase);
        boolean exists = webappFiles.exists();
        if (webappFiles.exists()) {
            File[] files = webappFiles.listFiles();
            for (File file : files) {
                String contextName = file.getName();
                Context context = new Context();
                //给context传值
                context.setName("/" + contextName);
                File[] files1 = file.listFiles();
                for (File file1 : files1) {
                    //解析web.xml
                    if (!file1.isDirectory()) {
                        InputStream fileInputStream = new FileInputStream(file1);
                        Document webDocument = saxReader.read(fileInputStream);
                        Element webRootElement = webDocument.getRootElement();
                        List<Element> servletNodes = webRootElement.selectNodes("//servlet");
                        for (Element element : servletNodes) {
                            Wrapper wrapper = new Wrapper();
                            Element servletNameElement = (Element) element.selectSingleNode("servlet-name");
                            String servletName = servletNameElement.getStringValue();
                            Element servletClassElement = (Element) element.selectSingleNode("servlet-class");
                            String servletClass = servletClassElement.getStringValue();

                            // 根据servlet-name的值找到url-pattern
                            Element servletMapping = (Element) webRootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                            //lagou
                            String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
                            wrapper.setUrl(urlPattern);
                            String servletPath = servletClass.replace(".", "/");
                            servletPath = servletPath.substring(0, servletPath.lastIndexOf("/") + 1);
                            MyClassLoader myClassLoader = new MyClassLoader(appBase + "/" + servletPath);
                            int index = servletClass.lastIndexOf(".") + 1;
                            String substring = servletClass.substring(index);
                            Class<?> clazz = myClassLoader.loadClass(substring);
                            wrapper.setHttpServlet((HttpServlet) clazz.newInstance());
                            context.getWrapperList().add(wrapper);
                        }

                    }
                }
                host.getContextList().add(context);
            }

            mapper.getHostList().add(host);

        }

    }


    /**
     * MiniCat的程序启动入口
     *
     * @param args
     */
    public static void main(String[] args) throws Exception {
        BootStrap bootStrap = new BootStrap();
        try {
            bootStrap.start();
        } catch (IOException | URISyntaxException e) {
            e.printStackTrace();
        }
    }
}
